﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using System.Reflection;
using System.ComponentModel;
using System.Web.UI;

namespace d39
{
    public static class ListViewEdit
    {
        public static void SetEditableListDataSource<T, TButton>(this ListView listView, List<T> list, TButton addButton, Action rebindUnchanged, Action rebindOnChange)
            where T : class, new()
            where TButton : Control, IButtonControl
        {
            listView.SetEditableListDataSource(list, () => new T(), addButton, rebindUnchanged, rebindOnChange);
        }

        public static void SetEditableListDataSource<T, TButton>(this ListView listView, List<T> list, Func<T> createNew, TButton addButton, Action rebindUnchanged, Action rebindOnChange)
            where T : class
            where TButton : Control, IButtonControl
        {
            rebindUnchanged = rebindUnchanged ?? (() => listView.DataBind());
            rebindOnChange = rebindOnChange ?? rebindUnchanged;

            var isAdding = (listView.EditIndex >= list.Count);
            listView.DataSource = getBindList(list, createNew, isAdding);

            listView.ItemEditing += (sender, e) =>
            {
                listView.EditIndex = e.NewEditIndex;
                rebindUnchanged();
            };

            listView.ItemCanceling += (sender, e) =>
            {
                listView.DataSource = list;
                listView.EditIndex = -1;
                rebindUnchanged();
            };

            listView.ItemDeleting += (sender, e) =>
            {
                list.RemoveAt(e.ItemIndex);
                rebindOnChange();
            };

            listView.ItemUpdating += (sender, e) =>
            {
                var editItem = isAdding ? createNew() : list[listView.EditIndex];
                foreach (string propertyName in e.NewValues.Keys)
                {
                    var value = e.NewValues[propertyName];
                    var prop = typeof(T).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
                    prop.SetValue(editItem, convertValue(value, prop.PropertyType), new object[] {});
                }
                if (isAdding) list.Add(editItem);
                listView.DataSource = list;
                listView.EditIndex = -1;
                rebindOnChange();
            };

            addButton.Click += (sender, e) =>
            {
                listView.DataSource = getBindList(list, createNew, true);
                listView.EditIndex = list.Count;
                rebindUnchanged();
            };
        }

        private static object convertValue(object value, Type type)
        {
            if (type.IsEnum && value is string)
            {
                return Enum.Parse(type, (string)value);
            }
            else if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null || (string)value == "")
                {
                    return null;
                }
                else
                {
                    return convertValue(value, type.GetGenericArguments()[0]);
                }
            }
            else
            {
                return Convert.ChangeType(value, type);
            }
        }

        private static List<T> getBindList<T>(List<T> list, Func<T> createNew, bool isAdding)
        {
            if (!isAdding) return list;

            var bindList = new List<T>(list);
            bindList.Add(createNew());
            return bindList;
        }
    }
}